﻿using System;
using System.Data;
using System.Xml;
using MySql.Data.MySqlClient;

/* ¡¡IMPORTANTE!! 
 De acuerdo al motor de BD utilizado, se deben utilizar unos u otros tipos de objetos.
 *** En SQL Server, se deben utilizar:
 * SqlConnection
 * SqlCommand
 * SqlDataAdapter
 * 
 *** En MySQL, se deben utilizar:
 * MySqlConnection
 * MySqlCommand
 * MySqlDataAdapter
 * 
 * Tener en cuenta los using que correspondieran.
 */
namespace capaDatos
{
    class BaseDatos
    {
        /* --------------------- Atributos de la clase --------------------- */
        // Datos básicos de configuración de la Conexión
        private string nombreBD = "", usuario = "", clave = "", servidor = "", cadenaConexion = "", motor = "";

        // Resultados de la interacción con la BD
        private MySqlConnection conexion = null;
        private MySqlCommand comando = null;
        private DataSet resultadoConsulta;
        private int filasAfectadas = -1;
        private string error = "";
        private int idGenerado = 0;


        /* ----------------------- Propiedades de la clase ----------------------- */
        public static string getMotorBD()
        {
            XmlElement nodoConexion = leerXML();
            return nodoConexion.GetElementsByTagName("motor")[0].InnerText.Trim();
        }

        public string getError()
        {
            return error;
        }

        public int getFilasAfectadas()
        {
            return filasAfectadas;
        }

        public DataSet getResultadoConsulta()
        {
            return resultadoConsulta;
        }

        public int getIdGenerado()
        {
            return this.idGenerado;
        }

        public BaseDatos()
        {
            // Se llama al método para cargar los valores de la conexión actual
            cargarDatosDesdeXML();

            if (this.cadenaConexion != "") this.conexion = new MySqlConnection(this.cadenaConexion);
            this.resultadoConsulta = new DataSet();
        }

        /* ------------------- Métodos de la clase ------------------- */
        private void cargarDatosDesdeXML()
        {
            try
            {
                XmlElement nodoConexion = leerXML();

                /* Lo que queda es tomar el objeto conexion y extraer sus atributos. */
                this.nombreBD = nodoConexion.GetElementsByTagName("nombreBD")[0].InnerText.Trim();
                this.usuario = nodoConexion.GetElementsByTagName("usuario")[0].InnerText.Trim();
                this.clave = nodoConexion.GetElementsByTagName("clave")[0].InnerText.Trim();
                this.servidor = nodoConexion.GetElementsByTagName("servidor")[0].InnerText.Trim();
                this.motor = nodoConexion.GetElementsByTagName("motor")[0].InnerText.Trim();
                string formatoCadenaConexion = nodoConexion.GetElementsByTagName("cadenaConexion")[0].InnerText.Trim();

                if (nombreBD != "" && usuario != "" && servidor != "")
                    //this.cadenaConexion = string.Format("Data Source={0};Initial Catalog={1};Persist Security Info=True;User ID={2};Password={3}", this.servidor, this.nombreBD, this.usuario, this.clave);
                    this.cadenaConexion = string.Format(formatoCadenaConexion, this.servidor, this.nombreBD, this.usuario, this.clave);
                else this.error = "Faltan datos para generar correctamente la cadena de conexión a la base de datos.";
            }
            catch (Exception ex)
            {
                this.error = "Ocurrió una excepción: " + ex.Message;
            }
        }

        private static XmlElement leerXML()
        {
            // Creo un objeto que será el que me permita manipular el archivo físico XML como un objeto
            XmlDocument doc = new XmlDocument();
            doc.Load("datosConexion.xml");

            // Accedo al nodo raíz (accesoDatos) para luego obtener el listado de Nodos conexion (que debiera ser sólo uno)
            // y a partir de éste acceder a sus atributos.
            /* Un nodo es un elemento que a su vez tiene elementos dentro de sus etiquetas, y los elementos 
             * son todos aquellas atributos que unicamente tienen valores dentro de sus etiquetas. */
            XmlNodeList accesoDatos = doc.GetElementsByTagName("accesoDatos"); // Raíz del documento XML
            XmlNodeList listaConexiones = ((XmlElement)accesoDatos[0]).GetElementsByTagName("conexion");
            XmlElement nodoConexion = (XmlElement)listaConexiones[0];

            return nodoConexion;
        }


        /** 
         * Objetivo: Establece una conexión con la BD especificada según los atributos de la clase.
         * Suposiciones: los atributos fueron establecidos previamente (en la creación del objeto)
         * Valor devuelto: booleano que indica si el proceso fue exitoso o no. Si no lo fue, puede consultarse el atributo
         * error con detalles sobre el mismo.
         * Última modificación: Enero 2013
         * Autor última modificación: Nanu */
        private void conectar()
        {
            this.error = "";
            try
            {
                if (conexion != null) conexion.Open();
                else this.error = "No pudo establecerse la conexión con la Base de Datos.";
            }
            catch (Exception ex)
            {
                this.error = string.Format("Ocurrió una excepción. Mensaje: {0}. Fuente: {1}", ex.Message, ex.Source);
            }
        }


        /** 
         * Objetivo: Libera la conexión con la BD y todos los recursos asociados.
         * Valor devuelto: booleano que indica si el proceso fue exitoso o no. Si no lo fue, puede consultarse el atributo
         * error con detalles sobre el mismo.
         * Última modificación: Enero 2013
         * Autor última modificación: Nanu */
        private void desconectar()
        {
            this.error = "";
            try
            {
                if (conexion != null) conexion.Close();
            }
            catch (Exception ex)
            {
                this.error = string.Format("Ocurrió una excepción. Mensaje: {0}. Fuente: {1}", ex.Message, ex.Source);
            }
        }


        /** Objetivo: Ejecuta una consulta SQL a la base de datos a la cual se está conectado.
         * El resultado se asigna al atributo de tipo DataSet de la clase. Éste debe consultarse para acceder a los resultados.
         * Última modificación: Enero 2013
         * Autor última modificación: Nanu */
        public DataSet consultar(string consultaSQL)
        {
            this.error = "";
            this.resultadoConsulta = new DataSet();
            try
            {
                this.conectar();
                if (this.error.Trim().Equals(""))
                {
                    // Si no existe el objeto comando, lo creo asociándole la consulta SQL. Si ya existiera, le asigno la consulta SQL.
                    if (this.comando == null) this.comando = new MySqlCommand(consultaSQL, this.conexion);
                    else this.comando.CommandText = consultaSQL;

                    MySqlDataAdapter adaptadorDatos = new MySqlDataAdapter(this.comando);
                    adaptadorDatos.Fill(this.resultadoConsulta);
                }
                this.desconectar();
            }
            catch (Exception ex)
            {
                this.error = string.Format("Ocurrió una excepción. Mensaje: {0}. Fuente: {1}", ex.Message, ex.Source);
            }

            return this.resultadoConsulta;
        }


        /**  Objetivo: Ejecuta una sentencia SQL del tipo INSERT, UPDATE o DELETE.
         * Las cantidad de filas afectadas (insertadas, modificadas o borradas) se asigna al atributo de tipo int 
         * filasAfectadas de la clase. Éste debe consultarse para conocer el resultado del proceso.
         * Última modificación: Enero 2013
         * Autor última modificación: Nanu */
        public void ejecutarSQL(String sentenciaSQL)
        {
            this.error = "";
            this.idGenerado = 0;
            try
            {
                this.conectar();
                if (this.error.Trim().Equals(""))
                {
                    bool esInsercion = sentenciaSQL.Trim().ToUpper().StartsWith("INSERT");
                    // En caso de tratarse de una inseción, permitirá conocer el último ID generado
                    if (esInsercion) sentenciaSQL += "; SELECT LAST_INSERT_ID()";

                    // Si no existe el objeto comando, lo creo asociándole la consulta SQL. Si ya existiera, le asigno la consulta SQL.
                    if (this.comando == null) this.comando = new MySqlCommand(sentenciaSQL, this.conexion);
                    else this.comando.CommandText = sentenciaSQL;

                    if (esInsercion) this.idGenerado = Convert.ToInt32(comando.ExecuteScalar());
                    else this.filasAfectadas = comando.ExecuteNonQuery();
                }
                else this.filasAfectadas = -1;
                this.desconectar();
            }
            catch (Exception ex)
            {
                this.error = string.Format("Ocurrió una excepción. Mensaje: {0}. Fuente: {1}", ex.Message, ex.Source);
                this.filasAfectadas = -1;
            }
        }

        /**  Objetivo: Ejecuta una sentencia SQL de tipo COUNT.
 * Devuelve un entero indicado cuántos registros cumplen con las condiciones especificadas por la sentencia SQL.
 * Última modificación: Abril 2013
 * Autor última modificación: Nanu */
        public int cantidadRegistros(String sentenciaSQL)
        {
            int cantidad = 0;
            this.error = "";
            try
            {
                this.conectar();
                if (this.error.Trim().Equals(""))
                {
                    // Si no existe el objeto comando, lo creo asociándole la consulta SQL. Si ya existiera, le asigno la consulta SQL.
                    if (this.comando == null) this.comando = new MySqlCommand(sentenciaSQL, this.conexion);
                    else this.comando.CommandText = sentenciaSQL;

                    cantidad = Convert.ToInt32(comando.ExecuteScalar());
                }

                this.desconectar();
            }
            catch (Exception ex)
            {
                this.error = string.Format("Ocurrió una excepción. Mensaje: {0}. Fuente: {1}", ex.Message, ex.Source);
            }

            return cantidad;
        }

        /**  Objetivo: Ejecuta una sentencia SQL de tipo MAX.
         * Devuelve un entero indicado el máximo número según las condiciones especificadas por la sentencia SQL.
         * Última modificación: Mayo 2013
         * Autor última modificación: Nanu */
        public int obtenerNumeroEntero(String sentenciaSQL)
        {
            int numero = 0;
            this.error = "";
            try
            {
                this.conectar();
                if (this.error.Trim().Equals(""))
                {
                    // Si no existe el objeto comando, lo creo asociándole la consulta SQL. Si ya existiera, le asigno la consulta SQL.
                    if (this.comando == null) this.comando = new MySqlCommand(sentenciaSQL, this.conexion);
                    else this.comando.CommandText = sentenciaSQL;

                    numero = Convert.ToInt32(comando.ExecuteScalar());
                }

                this.desconectar();
            }
            catch (Exception ex)
            {
                this.error = string.Format("Ocurrió una excepción. Mensaje: {0}. Fuente: {1}", ex.Message, ex.Source);
            }

            return numero;
        }
    }
}
